﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace StateManager
{
    public enum ScreenState
    {
        Active,
        Hidden
    }

    class SManager : DrawableGameComponent
    {



        // these lists are using the abstract class
        // this is to allow all of the screens that inherit it to be
        // stored in this code.  Polymorphism at work here.
        List<GenericScreenCode> screens = new List<GenericScreenCode>();
        List<GenericScreenCode> updateScreens = new List<GenericScreenCode>();

        // create an input object to get input from the controllers/keyboard
        Input input = new Input();

        SpriteBatch spriteBatch;

        Texture2D blankTexture;

        public SpriteBatch SpriteBatch
        {
            get { return spriteBatch; }
        }

        SpriteFont font;
        public SpriteFont SpriteFont
        {
            get { return font; }
        }
        //Texture2D blankT;

        bool isInitialized;

        // this property returns the playerIndex of the controller that activates the pause menu.
        // This is so that the other players cannot control the menu until the main menu is shown.
        // This property can also be null in case universial control is given.
        public PlayerIndex? ControllingPlayer
        {
            get { return controllingPlayer; }
            internal set { controllingPlayer = value; }
        }

        PlayerIndex? controllingPlayer;

        ScreenState sState;

        public bool IsActive
        {
            get
            {
                return !otherScreenHasFocus && sState == ScreenState.Active;
            }
        }

        bool otherScreenHasFocus;


        public SManager(Game1 game)
            : base(game)
        {

        }

        public override void Initialize()
        {
            base.Initialize();

            isInitialized = true;
        }


        protected override void LoadContent()
        {
            ContentManager content = Game.Content;
            spriteBatch = new SpriteBatch(GraphicsDevice);
            font = content.Load<SpriteFont>("menuFont");
            blankTexture = content.Load<Texture2D>("blank");

            foreach (GenericScreenCode screen in screens)
            {
                screen.LoadContent();
            }
        }

        protected override void UnloadContent() 
        {
            foreach (GenericScreenCode screen in screens)
            {
                screen.UnloadContent();
            }            
        }

        // returns the list of screens in memory as an array
        public GenericScreenCode[] GetScreens()
        {
            return screens.ToArray();
        }

        public void RemoveScreen(GenericScreenCode screen)
        {
            // If we have a graphics device, tell the screen to unload content.
            if (isInitialized)
            {
                screen.UnloadContent();
            }

            screens.Remove(screen);
            updateScreens.Remove(screen);

        }


        // updates the screens logic.  This screen is called regardless if the screen is the active one.
        public override void Update(GameTime gameTime)
        {
            // Read the keyboard and gamepad.
            input.Update();

            // Make a copy of the master screen list, to avoid confusion if
            // the process of updating one screen adds or removes others.
            updateScreens.Clear();

            foreach (GenericScreenCode screen in screens)
                updateScreens.Add(screen);

            bool otherScreenHasFocus = !Game.IsActive;
            bool coveredByOtherScreen = false;

            // Loop as long as there are screens waiting to be updated.
            while (updateScreens.Count > 0)
            {
                // Pop the topmost screen off the waiting list.
                GenericScreenCode screen = updateScreens[updateScreens.Count - 1];

                updateScreens.RemoveAt(updateScreens.Count - 1);

                // Update the screen.
                screen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

                if (screen.ScreenState == ScreenState.Active)
                {
                    // 
                    // 
                    if (!otherScreenHasFocus)
                    {
                        screen.HandleInput(input);

                        otherScreenHasFocus = true;
                    }

                    // 
                    // 
                    if (!screen.IsPopup)
                        coveredByOtherScreen = true;
                }
            }

        }

        // this method allows input to be given to the active screen.  This method is only called from the active
        // screen
        public virtual void HandleInput(Input input) { }



        public override void Draw(GameTime gameTime) 
        {
            foreach (GenericScreenCode screen in screens)
            {
                if (screen.ScreenState == ScreenState.Hidden)
                    continue;

                screen.Draw(gameTime);
            }
        }

        public void AddScreen(GenericScreenCode screen, PlayerIndex? controllingPlayer)
        {
            screen.ControllingPlayer = controllingPlayer;
            screen.SManager = this;
            screen.IsExiting = false;

            // If we have a graphics device, tell the screen to load content.
            if (isInitialized)
            {
                screen.LoadContent();
            }

            screens.Add(screen);
        }

    }
}
